#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "pear_redis.h"
#include "log/pear_log.h"

#define REDIS_DEFAULT_HOST    "127.0.0.1"
#define REDIS_DEFAULT_PORT    6379
#define REDIS_DEFAULT_TIMEOUT  1

#define CMD_LEN 256

static int pr_redis_reply_parse(redisReply *, const char *);

pr_redis_t *pr_new_redis(int timeout, char *host, int port)
{
    if (host == NULL && port == 0)
    {
        host = REDIS_DEFAULT_HOST;
        port = REDIS_DEFAULT_PORT;
        timeout = REDIS_DEFAULT_TIMEOUT;
    }
    else if (host == NULL || port == 0)
    {
        PEAR_LOG("Redis Arguments Error\n");
        return NULL;
    }

    pr_redis_t *temp = (pr_redis_t *) malloc(sizeof(pr_redis_t));
    if (temp == NULL)
    {
        return NULL;
    }

    temp->port = port;
    strcpy(temp->host, host);

    temp->tv.tv_sec  = timeout / 1000;
    temp->tv.tv_usec = timeout * 1000;

    temp->red_ctx = redisConnectWithTimeout(temp->host, temp->port, temp->tv);
    if (temp->red_ctx->err)
    {
        PEAR_LOG("Redis Connect Error: %s\n", temp->red_ctx->errstr);

        redisFree(temp->red_ctx);
        free(temp);

        return NULL;
    }

    return temp;
}

int pr_redis_set(pr_redis_t *reds, char *key, char *value)
{
    redisReply *reply = NULL;

    reply = redisCommand(reds->red_ctx, "SET key:%s %s", key, value);

    if (reply == NULL)
    {
        PEAR_LOG("redisCommand SET Error\n");
        //redisFree(reds->red_ctx);
        return -1;
    }

    if (!(reply->type == REDIS_REPLY_STATUS && strcasecmp(reply->str,"OK") == 0))
    {
        PEAR_LOG("Failed to execute command: SET key:%s %s ; ERROR: %s\n", key, value, reply->str);
        freeReplyObject(reply);
        //redisFree(reds->red_ctx);
        return -1;
    }

    freeReplyObject(reply);

    return 0;
}

int pr_redis_del(pr_redis_t *reds, char *key)
{
    redisReply *reply = NULL;

    reply = redisCommand(reds->red_ctx, "DEL key:%s", key);

    if (reply == NULL)
    {
        PEAR_LOG("redisCommand SET Error\n");
        //redisFree(reds->red_ctx);
        return -1;
    }
    if (reply->type != REDIS_REPLY_INTEGER)
    {
        PEAR_LOG("Failed to execute command: DEL key:%s ; ERROR: %s\n", key, reply->str);
        freeReplyObject(reply);
        //redisFree(reds->red_ctx);
        return -1;
    }

    freeReplyObject(reply);

    return 0;
}


char *pr_redis_get(pr_redis_t *reds, char *key)
{
    char       *buf   = NULL;
    redisReply *reply = NULL;

    reply = redisCommand(reds->red_ctx, "GET key:%s", key);

    if (reply == NULL)
    {
        PEAR_LOG("redisCommand GET Error\n");
        return NULL;
    }

    if (reply->type != REDIS_REPLY_STRING)
    {
        PEAR_LOG("Failed to execute command: GET key:%s ; ERROR: %s\n", key, reply->str);
        freeReplyObject(reply);
        //redisFree(reds->red_ctx);
        return NULL;
    }

    buf = (char *)malloc(reply->len * sizeof(char) + 1);
    if (buf == NULL)
    {
        freeReplyObject(reply);
        return NULL;
    }

    strncpy(buf, reply->str, reply->len);
    buf[reply->len + 1] = 0;

    freeReplyObject(reply);

    return buf;
}


/* Prase Redis Reply */
static int pr_redis_reply_parse(redisReply *reply, const char *cmd)
{
    int i = 0;
    if (NULL == cmd || reply == NULL)
    {
        PEAR_LOG("Invalid Parameter: reply or cmd is NULL\n");
        return -1;
    }

    PEAR_LOG("redisReply Type:%d\n", reply->type);

    int ret = -1;

    switch (reply->type)
    {
        case REDIS_REPLY_STRING:
                PEAR_LOG("Execute Command [%s] return string[%s]", cmd, reply->str);
                ret = 0;
                break;

        case REDIS_REPLY_ARRAY:
                PEAR_LOG("Execute Command [%s] return context:\n", cmd);
                redisReply* temp =  NULL;
                for(i = 0; i < reply->elements; i++)
                {               
                    temp = reply->element[i];
                    PEAR_LOG("(%d) %s\n", temp->type, temp->str);
                }
                ret = 0;
                break;

        case REDIS_REPLY_INTEGER:
                PEAR_LOG("Execute Command [%s] return intger[%lld].\n", cmd, reply->integer);
                ret = 0;
                break;

        case REDIS_REPLY_NIL:
                PEAR_LOG("Execute Command [%s] return nil.\n", cmd);
                ret = -1;
                break;

        case REDIS_REPLY_STATUS:
                PEAR_LOG("Execute Command [%s] return status[%s].\n", cmd, reply->str);
                ret = 0;
                break;

        case REDIS_REPLY_ERROR:
                PEAR_LOG("Execute Command [%s] return error[%s].\n", cmd, reply->str);
                ret = -1;
                break;

        default:
                PEAR_LOG("Execute Command [%s] return unknown type\n", cmd);
                ret = -1;
                break;
    }

    return ret;
}
